﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using FinanSoft.Utilities;
using FinanSoft.BusinessLayer;
using FinanSoft.Entities;

namespace FinanSoft
{
    public partial class frmReporte : Form
    {
        private Currency _reportCurrency;
        private Constants.ReportType _reportType;
        private double _credit;
        private double _debit;
        private int _reportLevel;

        public frmReporte(Constants.ReportType reportType, bool isSeason, int timeId, int moneyId, int reportLevel)
        {
            _reportLevel = reportLevel;
            _reportType = reportType;
            _reportCurrency = MoneyManager.GetInstance().GetCurrency(moneyId);
            // Inicializa el titulo del reporte
            String title = "";
            Account[] reportAccounts = new Account[0];
            InitializeComponent();
            txtReporte.Text = title;
            PeriodManager man = PeriodManager.GetInstance();
            txtCompania.Text = ReportManager.GetInstance().LoadedCompany.Name;
            if (isSeason)
                txtPeriodo.Text = man.GetSeason(timeId).Name;
            else
                txtPeriodo.Text = man.GetPeriod(timeId).Name;
            if (reportType == Constants.ReportType.balanceSheet)
            {
                title = MessageConstants.BALANCE_SHEET_TITLE;
                reportAccounts = isSeason ? ReportManager.GetInstance().GetSeasonBalanceSheet(timeId, _reportCurrency.Id) :
                    ReportManager.GetInstance().GetPeriodBalanceSheet(timeId, _reportCurrency.Id);
                buildBalanceSheet(reportAccounts);
            }
            else if (reportType == Constants.ReportType.incomeStatement)
            {
                title = MessageConstants.INCOME_STATEMENT_TITLE;
                List<Account> specialAccounts;
                reportAccounts = isSeason ? ReportManager.GetInstance().GetSeasonIncomeStatement(timeId, _reportCurrency.Id, out specialAccounts) :
                    ReportManager.GetInstance().GetPeriodIncomeStatement(timeId, _reportCurrency.Id, out specialAccounts);
                buildIncomeStatement(reportAccounts, specialAccounts);
            }
            else if (reportType == Constants.ReportType.trialBalance)
            {
                title = MessageConstants.TRIAL_BALANCE_TITLE;
                reportAccounts = isSeason ? ReportManager.GetInstance().GetSeasonTrialBalance(timeId, _reportCurrency.Id) :
                    ReportManager.GetInstance().GetPeriodTrialBalance(timeId, _reportCurrency.Id) ;
                buildTrialBalance(reportAccounts);
            }
        }

        private void buildTrialBalance(Account[] reportAccounts)
        {
            _debit = 0;
            _credit = 0;
            foreach (Account account in reportAccounts)
                addAccount(account, 0);
            gridReporte.Rows.Add(new object[] { "TOTAL:", _reportCurrency.Symbol + _debit.ToString(), _reportCurrency.Symbol + _credit.ToString() });
        }

        private void buildBalanceSheet(Account[] reportAccounts)
        {
            _debit = 0;
            _credit = 0;
            double totalAssets = 0;
            foreach (Account account in reportAccounts)
                if (account.AccountType == Constants.AccountType.active)
                {
                    addAccount(account, 0);
                    totalAssets += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? 1 : -1);
                }
            gridReporte.Rows.Add(new object[] { "TOTAL ACTIVOS:", _reportCurrency.Symbol+ totalAssets.ToString(), " "});
            double totalPassives = 0;
            foreach (Account account in reportAccounts)
                if (account.AccountType == Constants.AccountType.passive)
                {
                    addAccount(account, 0);
                    totalPassives += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                }
            gridReporte.Rows.Add(new object[] { "TOTAL PASIVOS:", " ", _reportCurrency.Symbol + totalPassives.ToString() });
            double totalCapital = 0;
            foreach (Account account in reportAccounts)
                if (account.AccountType == Constants.AccountType.capital)
                {
                    addAccount(account, 0);
                    totalCapital += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                }
            gridReporte.Rows.Add(new object[] { "TOTAL PASIVOS Y CAPITAL:", " ", _reportCurrency.Symbol + (totalCapital + totalPassives).ToString() });
        }

        private void buildIncomeStatement(Account[] reportAccounts, List<Account> specialAccounts)
        {
            _debit = 0;
            _credit = 0;
            double totalIncome = 0;
            // Calculamos todos los ingresos operativos exceptuando ventas
            gridReporte.Rows.Add(new object[] { "INGRESOS OPERATIVOS", " ", " " });
            foreach (Account account in reportAccounts)
                if (account.AccountType == Constants.AccountType.income || account.AccountType == Constants.AccountType.oincome)
                {
                    addAccount(account, 0);
                    totalIncome += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                }
            // Calculamos todos los ingresos por ventas
            bool salesExist = false;
            double totalSales = 0;
            foreach (Account account in specialAccounts)
                if (account.SpecialType == Constants.SpecialType.sale || account.SpecialType == Constants.SpecialType.salesRefund)
                {
                    if (!salesExist)
                    {
                        gridReporte.Rows.Add(new object[] { "INGRESOS POR VENTAS", " ", " " });
                        salesExist = true;
                    }
                    addAccount(account, 0);
                    totalSales += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                }
            if (salesExist)
                gridReporte.Rows.Add(new object[] { "UTILIDAD BRUTA EN VENTAS:", " ", _reportCurrency.Symbol + totalSales.ToString() });
            gridReporte.Rows.Add(new object[] { "INGRESOS OPERATIVOS TOTALES:", " ", _reportCurrency.Symbol + (totalIncome + totalSales).ToString() });
            // Calculamos todos los egresos operativos
            double totalOutcome = 0;
            gridReporte.Rows.Add(new object[] { "EGRESOS OPERATIVOS", " ", " " });
            foreach (Account account in reportAccounts)
                if (account.AccountType == Constants.AccountType.outcome || account.AccountType == Constants.AccountType.ooutcome)
                {
                    addAccount(account, 0);
                    totalOutcome += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? 1 : -1);
                }
            gridReporte.Rows.Add(new object[] { "EGRESOS OPERATIVOS TOTALES:", _reportCurrency.Symbol + totalOutcome.ToString(), " " });
            double utilities = totalIncome+totalSales - totalOutcome;
            gridReporte.Rows.Add(new object[] { "UTILIDAD OPERATIVA:", utilities < 0 ? (_reportCurrency.Symbol + "(" + (utilities * -1).ToString() + ")") : _reportCurrency.Symbol + utilities.ToString(), " " });
            // Calculamos las partidas extraordinarias
            bool extraordinaryExist = false;
            double totalExtraordinaries = 0;
            foreach (Account account in specialAccounts)
                if (account.SpecialType == Constants.SpecialType.extraordinaryIncome)
                {
                    if (!extraordinaryExist)
                    {
                        gridReporte.Rows.Add(new object[] { "PARTIDAS EXTRAORDINARIAS", " ", " " });
                        extraordinaryExist = true;
                    }
                    totalExtraordinaries += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                    account.AccountType = Constants.AccountType.income;
                    addAccount(account, 0);
                }
            utilities += totalExtraordinaries;
            // Calculamos los impuestos
            bool taxExist = false;
            double totalTaxes = 0;
            foreach (Account account in specialAccounts)
                if (account.SpecialType == Constants.SpecialType.incomeTax)
                {
                    if (!taxExist)
                    {
                        gridReporte.Rows.Add(new object[] { "UTILIDAD ANTES DE IMPUESTOS:", " ", utilities < 0 ? (_reportCurrency.Symbol + "(" + (utilities * -1).ToString() + ")") : _reportCurrency.Symbol + utilities.ToString() });
                        taxExist = true;
                    }
                    totalTaxes += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                    account.AccountType = Constants.AccountType.income;
                    addAccount(account, 0);
                }
            utilities += totalTaxes;
            gridReporte.Rows.Add(new object[] { "UTILIDAD NETA:", " ", utilities < 0 ? (_reportCurrency.Symbol + "(" + (utilities * -1).ToString() + ")") : _reportCurrency.Symbol + utilities.ToString() });
            double totalOther = 0;
            foreach (Account account in specialAccounts)
                if (account.SpecialType == Constants.SpecialType.dividends || account.SpecialType == Constants.SpecialType.retainedEarnings)
                {
                    totalOther += (_reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? account.SysAmount : account.LocalAmount) * (account.AlDebe ? -1 : 1);
                    account.AccountType = Constants.AccountType.income;
                    addAccount(account, 0);
                }
            utilities += totalOther;
            gridReporte.Rows.Add(new object[] { "UTILIDAD DEL PERIODO:", " ", utilities < 0 ? (_reportCurrency.Symbol + "(" + (utilities * -1).ToString() + ")") : _reportCurrency.Symbol + utilities.ToString() });
        }

        private void addAccount(Account acc, int level)
        {

            if (!acc.IsTitle || level + 1 == _reportLevel)
            {
                double accBalance = _reportCurrency.Id == MoneyManager.GetInstance().SystemCurrency.Id ? acc.SysAmount : acc.LocalAmount;
                if (accBalance == 0) return;
                if (acc.AlDebe)
                    _debit += accBalance;
                else
                    _credit += accBalance;
                String balance = accBalance.ToString();
                if ((_reportType == Constants.ReportType.balanceSheet || _reportType == Constants.ReportType.incomeStatement) &&
                        ((acc.AccountType == Constants.AccountType.active || acc.AccountType == Constants.AccountType.outcome || acc.AccountType == Constants.AccountType.ooutcome) ^ acc.AlDebe))
                {
                    balance = "(" + balance + ")";
                    acc.AlDebe = !acc.AlDebe;
                }
                if (acc.AlDebe && (acc.SpecialType == Constants.SpecialType.salesRefund || acc.SpecialType == Constants.SpecialType.sale))
                    balance = "(" + balance + ")";
                
                gridReporte.Rows.Add(new object[] { FormUtilities.AddTabs(acc.Name, level), acc.AlDebe ? balance : " ", !acc.AlDebe ? balance : " " });
            }
            else
            {
                gridReporte.Rows.Add(new object[] { FormUtilities.AddTabs(acc.Name, level), " ", " " });
                foreach(Account child in acc.Children.Values)
                    addAccount(child, level+1);
            }
        }
    }
}
